Ray Tracing in One Weekend
reference: Ray Tracing in One Weekend あとから知ったが日本語版もあるらしいappbird.icon
AC.icon やりきった!
所要時間:3日 (13 hours 41min)
https://scrapbox.io/files/66b39f252323f7001cc29f91.png
方針
指示にはできるだけ従う
あまり深く掘り下げない
下のメモは初見で読んでわかるように書いていない(時間がかかるので)
referenceを読み進めると同時に下のメモを使うのがちょうど良いと思うappbird.icon
AC.icon 2. Output an Image
ビルド環境設定
CMakeListsを自力で立てます
本来は資料側が用意してくれてるんだけど復習がてら
Hello World!
int32_t ir = int32_t(255.999 * r);
なぜ255.999? ---> 値を256にしたくないため、 int32_t(255.999 * r)の最大値は255。
code:log
❯ cmake --build build
Error: could not load cache
どうして
あ、CMakeListsのファイル名がCMakeLists.txtになってなかった
解決しない
Next, navigate to that build directory and run cmake to configure the project and generate a native build system
あ、cmakeでnative build systemを生成する過程をやってなかった
code:log
cd ./build
cmake ../
出せた
code:log
cmake --build build
./build/main > dst/hello.ppm
閲覧には
なんか黒一色なんですけど!?
出力方式間違えてた
ppmをどうみる問題
set(CMAKE_CXX_STANDARD 17)
https://scrapbox.io/files/66b0a79064e5da001c6809fe.png
実はこの画像色の遷移方向を誤っている(本来x方向に赤が強くなるべき)
std::clog << "\rScanlines remaining: " << (image_height - i) << " " << std::flush;
なんでいちいち改行せず\rを入れるんだ?
\r --> 行頭にカーソルを移動させる
同じ行に上書きができる ---> stdcerrが溢れかえらない
clogを使う理由は?cerrでよくない?
The major difference is that std::cerr is not buffered like the other two.
なるほどappbird.icon
AC.icon 3. The vec3 Class
Vectorクラスを作る
We’ll use the same class vec3 for colors, locations, directions, offsets, whatever. Some people don’t like this because it doesn’t prevent you from doing something silly, like subtracting a position from a color.
They have a good point, but we’re going to always take the “less code” route when not obviously wrong.
なるほどappbird.icon
Vectorクラスを作ることは目的ではないのでここはコピペする
write_color関数作る
AC.icon 4. Rays, a Simple Camera, and Background
目標:カメラからビューポートに向かってレイを放つ
Rayクラスを作るらしい
ray::at(t) この関数作ってどないするんや?
ここはちゃんと手で実装
クラスの設計的にrayはImmutableっぽいな(外部からフィールドをreadする方法はあるけどwriteする方法がない) gotten into trouble using square images for debugging because I transpose 𝑥 and 𝑦 too often, so we’ll use a non-square image.
あぁ〜...確かに正方形だとデバッグしにくいわ
現にさっき今バグ起こしたしな〜
The distance between two adjacent pixels is called the pixel spacing, and square pixels is the standard.
そういう名前ついてるんだ
If you're wondering why we don't just use aspect_ratio when computing viewport_width,...
なるほどこれ考えてなかったわ 切り捨て誤差か
1より小さくなることを防ぎたいなら、std::min(image_width / aspect_ratio, 1)を使えばもっとコードが短くなりそう
This distance is often referred to as the focal length.
viewportとカメラの距離ってfocal lengthっていうんだ
焦点距離みたいなもんかな? というかそれそのものだったわ どうやらそれとはまた違うみたい(13章)
AC.icon 5. Adding a Sphere
目標:カメラから放ったレイがある一つのオブジェクトに当たるかを調べ、当たったレイを画面に反映する。
空間上に一つ球を置いてその形を取る
飛ばしたレイが球に当たるかを判定するためには二次方程式を解けば良い
今回は、球当たったレイに対応するピクセルを赤く色付ける
ああ、四章で描いたのって空だったんだなappbird.icon
なんか楕円出てきた!w
double viewport_width = viewport_height * (double(image_height) / image_width);
(double(image_height) / image_width); --> 逆だった
https://scrapbox.io/files/66b0c930fe5614001d1e110c.png
うまくいった!
これ$ tの値が正か負かを考慮できていないのでは?
やっぱりそのようだった
you will get exactly the same picture because this solution doesn't distinguish between objects in front of the camera and objects behind the camera.
AC.icon 6. Surface Normals and Multiple Objects
目的:法線ベクトルを計算し図示する
https://scrapbox.io/files/66b18f58b65368001dc46dbc.png
レイが当たった箇所を調べないとその場所の方線を考えることができない ---> そこでray::at(t)だった
球の法線は簡単に求められる
単位長の法線ベクトル$ \bm nに対して$ \frac{1}{2}\left( \bm n + \left(\begin{array}{c}1 \\ 1 \\ 1\end{array}\right) \right)をRGB値とする。
そんで二次方程式の解で一次係数が2を因数として含む時にシンプルにかけることを活かせば、代数的に計算をもっと楽にできる
AC.icon 6.3 An Abstraction for Hittable Objects
一つの球も複数の球もhittableなものとして扱うらしい(抽象クラス) はぇ〜appbird.icon
code:c++
virtual bool hit(
const ray& r,
double ray_tmin,
double ray_tmax,
hit_record& rec
) = 0;
ray_tmin, ray_tmaxってなんでここで渡すんだろ?
どういう使い方をするんだろう
面が表か裏かを判定する ---> 今回は交差がわかったタイミングで情報を保存するようにする。
AC.icon 6.5 A List of Hittable Objects
Hittableの集合もHittableでなくてはならない
closest_so_farこの命名いいな 自分も使おうappbird.icon
code:cpp
for (const auto& object : objects) {
hit_record temp_rec;
if (object->hit(r, ray_tmin, closest_so_far, temp_rec)) {
...
}
}
ほほ〜〜なるほど、ここでray_tmaxが生きてくるのか
closest_so_farを渡し続けることで探索範囲を狭め続けるのか
ray_tminは球だけだったら0で渡す必要がないだろうけど多分オブジェクトごとによって違うんだろうね
高速化するためには?
hittable_listが整列されていれば二分探索してO(logN)ぐらいで済みそうだけど
どういう基準にして整列するのかがいまいちピンとこないな
作ったHittableとかどうやってmainに混ぜんのんappbird.icon
AC.icon 6.6 Some New C++ Features
知ってるから流し読み
あれ?でもまだusingしたmake_sharedって使ってないよね?
because it allows multiple geometries to share a common instance (for example, a bunch of spheres that all use the same color material), and because it makes memory management automatic and easier to reason about.
あぁ、そういう意図を持ってスマートポインタ使ってたのね
bunch of spheres that all use the same color material
うんうんappbird.icon
AC.icon 6.7 Common Constants and Utility Functions
For now we only need infinity, but we...
そうなの!?無限使うの?
ray_tmaxの初期値にINF設定するつもりか
constexpr double infinity = std::numeric_limits<double>::infinity();
これってそんな書き方できるんだ....appbird.icon
piってリテラルで書かなきゃダメだっけ
あっ...C++20からなのかこれ
degtoradって標準関数にないのか 初めて知った...。
rtweekendってなんだ?
あ〜ただ、VSCodeの関係上各ファイルでコードが成立するようにヘッダーファイル読み込んでおかないとダメだ
だからmain.cppを除いてヘッダファイルの構成は変えないようにする
いえーい
https://scrapbox.io/files/66b1c0570db9a5001d4dc2bb.png
下の地面(クソでかい球)の緑は上方向に法線ベクトルが向いてるってことですね
仮に球の内部にいたとしてその下側に立ってたら、それもまた緑方向を向くはず
今回の法線ベクトルはそのように定めたので(レイの飛んでくる方向に法線を向ける)、別途front_faceでどっちが表面か裏面かを区別する必要がある。
intervalクラスを定義
CameraクラスでRenderしよう
確かあの時も画面の内容に書き込む役割をCameraクラスに分担した気がする。
code:cpp
int main() {
// World
hittable_list world;
world.add(make_shared<sphere>(point3{0, 0, -1}, 0.5));
world.add(make_shared<sphere>(point3{0, -100.5, -1}, 100));
camera cam;
cam.aspect_ratio = 16.0 / 9.0;
cam.image_width = 400;
cam.render(world);
}
おぉ〜〜めっちゃスッキリ!
AC.icon 8. Antialiasing
この段階でそれやるのかappbird.icon
We can get the same effect by averaging a bunch of samples for each pixel.
一つのピクセルあたり何個もレイを飛ばすのかな
あ、ガチでサンプリングするのか(適当に計算しやすいピクセル内の4点を取るのかと思ってた)
There are no guarantees as to the quality of the random sequence produced. In the past, some implementations of rand() have had serious shortcomings in the randomness, distribution and period of the sequence produced (in one well-known example, the low-order bit simply alternated between 1 and 0 between calls).
とは書いてあるけどまあ今回の場合乱数の質に問題があってもさほど問題ないし大丈夫っちゃ大丈夫
ひとつ思ったけどdouble型の値の上で一様分布する数って本当に一様分布なのか?
本来の一様分布では、異なる二つの区間であって長さが同一なものに分布する確率は等しいはず
だけど、double型では異なる二つの区間であって長さが同一なものに含まれるdouble型の値は異なるのでは
例えば、0から2^{10}と、2^{10}から2*2^{10}に含まれるdouble型の値の数は異なるのでは?
この条件下だと上の条件(本来の一様分布では〜)は満たせていないような....。
32-bit Mersenne Twister by Matsumoto and Nishimura, 1998
なるほど、わからんappbird.icon
ともかく、サンプリングするために乱数生成器を作って
ピクセル(a Little Square)から数点サンプリングしてくる
そしてそれを平均する
この時、最終的な色の強度値が0, 1に収まるようにclampを挟んでおく
initialize系の関数使う時に、全てのフィールドを初期化できていないのでは?と疑いたくなるんだけどこれに対してどうにか対策できないだろうか
まず考えられるのはstd::optionalをつけるという方法 セットされていなければstd::noneがセットされているのでその段階で弾ける
だけどこれは大変appbird.icon
実行する前に全てのstd::optionalをassertするのか?と言われるとうーん...となる
optionalに何らかの値がセットされていることを保証する関数を用意するとちょっと管理が楽になるかもしれん
参照する時にいちいち*ってつけなきゃいけないのもめんどいな〜
関数でstd::optionalがついていないバージョンの変数をいちいち宣言するのもめんどいしな
オブジェクト消えて草 なんでだ......。
code:cpp
const vec3 pixel_sample = pixel00_loc
+ ((i + offset.x()) * pixel_delta_u)
+ ((j + offset.y()) * pixel_delta_u);
// ^
あっ
レイトレが時間かかるって言われてるのはここが原因か〜appbird.icon
10回ぐらいやってこれ
実行時間 0.48 s
MacBook Pro(2023) -- Apple M2 Pro
https://scrapbox.io/files/66b1d76714b84e001da2b98d.png
100回ぐらいやってこれ
実行時間 4.05 s
試行回数に対して線形時間かかるよな〜appbird.icon
https://scrapbox.io/files/66b1d88e0c3040001c253791.png
なるほど確かにより良い画像になってる...appbird.icon
AC.icon 9. Diffuse Materials
AC.icon 9.1 A Simple Diffuse Material
おお、いよいよか...。appbird.icon
Really any algorithm that randomizes direction will produce surfaces that look matte.
まじかいappbird.icon
物体に当たったら、レイをランダムな方向に拡散させる
ランダムな方向って、どうやって?
物体の内側に飛んでいくレイがあるとまずい
衝突点を中心とし単位球から一点サンプリングしてそのベクトルの方向にレイを飛ばす
単位球からサンプリングする方法 ---> 単位立方体からサンプリングしてそれが単位球上になければもう一回サンプリングする。
だいぶゴリ押しappbird.icon
三次元空間の回転を使えば高速化できるのだろうか...?appbird.icon
もしレイが物体の内側に飛んでいくようなら、その逆ベクトルを取る
code:cpp
color ray_color(
const ray& r,
const hittable& world
) const {
// Hittableに衝突したときの、その位置に関する情報
hit_record rec;
if (world.hit(r, interval{0, infinity}, rec)) {
vec3 direction = random_on_hemisphere(rec.normal);
return 0.5 * ray_color(ray{rec.p, direction}, world);
}
vec3 unit_direction = unit_vector(r.direction());
const double a = 0.5 * (unit_direction.y() + 1.0);
return (1 - a)*color{1.0, 1.0, 1.0} + a*color{0.5, 0.7, 1.0};
}
再帰関数マジかい
一応理論上は無限回跳ね返るってことか
無限遠にレイが飛んでいったら終了ってことかな
1. 無限遠にレイが飛んでいった時、ray_colorの戻り値は空の色になる(空から飛んでくる光線)
2. そのray_colorを呼び出した"前のray_color"がその色に反射率0.5を掛ける(1回反射した光線)
3. そのray_colorを呼び出した"前のray_color"がその色に反射率0.5を掛ける(2回反射した光線)
....
n. 最終的に反射した光がカメラに戻る(n-2回反射した光線)
確かに光が空から飛んできて反射し続ける過程になってる.....。appbird.icon
吸収の過程とかコードに実際に現れるのマジでおもろいなappbird.icon
code:log
Scanlines remaining: 178 zsh: segmentation fault ./build/main > dst/sphere.ppm
ンガアアアアアア
どうして....
セグフォはおかしくない...?appbird.icon ポインタとか範囲外参照とかした覚えないんだけどな
178で毎回止まるな、なんでだ?
Interruptionも効かないし
再帰をなくすとうまく動く
code:log
-0.0396783 0.423753 -0.737589
sampled
-0.0396783 0.423753 -0.737589
sampled
-0.0396783 0.423753 -0.737589
sampled
-0.0396783 0.423753 -0.737589
sampled
-0.0396783 0.423753 -0.737589
sampled
見た感じ同じ箇所で衝突を続けている様子。
code:sphere.hpp
if (root < ray_t.min) { root = (b + sqrt_d) / a; }
if (root < ray_t.min or ray_t.max < root) { return false; }
ここか....
なんでこれダメなんだろう?
あ、不等号に等号いるじゃんこれ
https://scrapbox.io/files/66b1e6ca27cbff001ce9648a.png
n = 20
拡散反射を再現するにあたって、反射する方向をランダムにとるからこうなるんだなappbird.icon
球に当たったレイが地面方向に飛んでいくと反射のたびに明るさが減るのでめちゃくちゃ暗くなる
それが球中の黒い点(ノイズっぽい点)になる
観察
地面を見てみると上方向から来た光(環境光)を受け、一部がほんのり青づいている
レイの挙動と合致している
地面と球の隣接部分がかなり暗い
他の場所と比べてレイがオブジェクトと反射しやすい場所にあり、そのたびIntensityが減る
人間が暗いところを見ている時、明るさに揺らぎを感じるのもこれが原因?
飛ばすレイの数を100にしてみるとこんな感じ。
レンダリング時間 20秒
最後あたりの行になるたびにどんどんレンダリング時間が長くなっていった
多分、球と地面の間に挟まるレイが多くなるため?
結構マシになってきた
https://scrapbox.io/files/66b1e8193e0393001df2f3d6.png
AC.icon 9.2. Limiting the Number of Child Rays
目標:反射回数を制限する
デスヨネーappbird.icon
再帰関数に残り反射回数を引数に乗せればOK
これ単体ではあまり計算時間は減らない
code:log
❯ time ./build/main > dst/sphere.ppm
Done.
./build/main > dst/sphere.ppm 19.33s user 0.01s system 96% cpu 20.012 total
AC.icon9.3. Fixing Shadow Acne
acneって何? ---> にきびのことらしい
なんこれappbird.icon
There’s also a subtle bug that we need to address.
バグあるんすか!?
あ〜やっぱり、ありえん近い場所にヒットしてしまうケースか...。
というわけで、衝突時にあまりに近い場所にヒットするような$ tは無視するようにする
code:log
❯ cmake --build build --target main && time ./build/main > dst/sphere.ppm
2/2 Linking CXX executable main Done.
./build/main > dst/sphere.ppm 9.26s user 0.01s system 94% cpu 9.802 total
おお、計算時間減った!明るくなった!
思った以上に近い場所で反射しまくってたっぽいなappbird.icon
https://scrapbox.io/files/66b1ef6de936930022385de6.png
AC.icon9.4 True Lambertian Reflection
なっ、何ーーーーーッ!!!
今まで見ていたDiffuse Reflectionは!?
Lamb....なんて?appbird.icon
ランバーティアンリフレクションと読むようだ
どうやら一様ランダム反射モデルよりももっといいモデルがあるらしい
This distribution scatters reflected rays in a manner that is proportional to $ \cos(\phi), where $ \phiis the angle between the reflected ray and the surface normal.
$ \cos\phiに比例するような確率分布に従うような反射角$ \phiが、本来のマットの挙動のようだ。
こういう意味ではない...?
実装難しそう
ところがどっこい長さ1の法線ベクトルからサンプリングした長さ1のベクトルを足した方向にレイを放てばこのような法則を満たすらしい。
まじか でも言われてみるとそんな気がする...!?
WA.icon 証明したい
そもそも示すべきことはなんだろう?
$ \phiの確率密度関数$ p(x = \phi)が$ \cos xに比例する。 これ示そうと思ったけどなぜか矛盾するんだよなappbird.icon
$ \thetaを法線ベクトルの先から伸ばした単位ベクトルの角度(二次元上)とすれば、$ \phi = \frac{\pi}2 - \frac \theta 2なんだよな...?
$ \thetaは今回一様分布になるはずなのだから、$ \phiも一様分布にならないか?
という形でよくわかっていないappbird.icon
https://scrapbox.io/files/66b2142452e549001c290220.png
AC.icon]9.5 Using Gamma Correction for Accurate Color Intensity
50%の時の画素のHSL 216°, 22%, 27%
確かに50%じゃない...appbird.icon
If you look closely, or if you use a color picker, you should notice that the 50% reflectance render (the one in the middle) is far too dark to be half-way between white and black (middle-gray). Indeed, the 70% reflector is closer to middle-gray.
ほんまや
Image viewerがGamma SpaceでIntensityが書かれていることを前提として表示しているからそうなっているとappbird.icon
人間の知覚において明るさ$ x \in [0, 1)を感じるには、実際には$ x^{\gamma}のエネルギー量を浴びさせればよいというもの
ええっと、つまり今どういう状況になってるんだろう?
本来あるべき光よりも低い光が出てしまっているということは、画像ビューアが期待しているのは上でいう$ xで、今回$ x^{\gamma}の方を渡してしまっているということになる。
$ x^{2\gamma}が出力されてしまっている。
画像ビューアは人間が感じる明るさの強度を入力として求めている
今回レイトレーシングの結果としてでてきたIntensityはエネルギー強度を表している
で、それを人間の知覚する明るさとして画像ビューアが解釈してしまっているので余計に暗く見えている
これに対処するには、最後レイトレの結果として出てきたエネルギー強度を人間の知覚する明るさ$ x^{1/\gamma}に矯正すればよい
今回はGamma 2を使う($ \gamma = 2)
Gamma Space $ x ---> Linear Space $ x^{\gamma}
Linear Space $ x ---> Space Space $ x^{1/\gamma}
おお、確かに明度が0.5になった
213°, 13%, 54%
輝度で測ったほうがいいのかな...?(輝度だと60%)
https://scrapbox.io/files/66b21d086757e1001c66086a.png
AC.icon 10. Metal
Material
ここまでくるとBlenderでいうMaterialが実際にはどういうプログラムなのかがようやく理解できるなappbird.icon
ぶつかったレイに対してどう色付けするかを定めるデータ構造なわけだ
attenuationって何? 減衰とのことらしい
衝突情報にマテリアルへの参照も載せて、レイの色を確認する時にとるっぽい
10.3 Modeling Light Scatter and Reflectance
albedo ... some form of fractional reflectance
ここでのfractionalってなんぞや?部分的な?
lambertian(const color& albedo) : albedo(albedo) {}
colorがalbedoってどういうこっちゃ?
あ、もしかしてRGB成分ごとにどれだけ吸収するかを定められるってことかい?
Albedo will vary with material color and (as we will later implement for glass materials) can also vary with incident viewing direction (the direction of the incoming ray).
ほぉappbird.icon
このクラス定義でどう使うんだろう
attenuationとscatteredをどう使うのか、戻り値のtrueは何?
attenuationは出力時の色に掛けるため(アダマール積: 要素ごとの掛け算) scatteredは反射光の方向を有向線分で示す。
戻り値は光が反射されるかされないかを表す。
つまり、materialクラスは物体に当たった光をどう取り扱うかの情報を提供するためのクラス!
scatteredの方向はよくみると0ベクトルになる可能性がある
0に近いベクトルの時は
10.4 Mirrored Light Reflection
入射ベクトルを$ \bm v、法線ベクトルを$ \bm nとしたとき、$ \bm v - 2(\bm v \cdot \bm n)\bm nと表せる
これを活かせばいい
というわけで、それで定義できたMetalマテリアルを使ったワールドを定義
なんか真っ黒な画像が出てくるんですけど!しかも処理やけに思いし!
球がデカすぎて自分が内部にいるだけだった
光が入ってこない
https://scrapbox.io/files/66b23a4fc5de14001dc5f8ff.png
ちなみに、Metalの反射方向をちょっとずらせばfuzzの入った反射も実装できる
このとき、fuzzした影響で法線の逆の方向を指してしまった場合は戻り値でfalseを返しておく
https://scrapbox.io/files/66b2385e5851a0001c8b7402.png
気になること
これ、ぼや〜っと光る光源を再現するにはどういうマテリアルの処理を書くと良いのだろうか?
光らせること自体は簡単にできそうだけど、ぼや〜っとさせるにはポストエフェクトによる工夫が必要そう Unityで絵作りする時になんでポストエフェクトがいるんだって思ってたけど納得できた
水面を作るには?
確かにハイトマップがいるappbird.icon
昔齧ったことに色々納得がいくようになってきた!
AC.icon 11 Dielectrics
Clear materials such as water, glass, and diamond are dielectrics. When a light ray hits them, it splits into a reflected ray and a refracted (transmitted) ray.
おぉ....appbird.icon 次はここプログラミングするのか...。ワクワクするぞ
reflected ray / refracted ray
スネルの法則を用いて、反射の方向をベクトル方程式で表したい
いちいち座標系を定義して求めるのはめんどっちいので、ベクトルの演算だけでどうにか済ませたい
証明
code:tex
\begin{aligned}
& \mathbf{R}^{\prime}{ }_{\perp}=\frac{\eta}{\eta^{\prime}}(\mathbf{R}+\cos \theta \mathbf{n}) \\
& \mathbf{R}^{\prime}{ }_{/ /}=-\sqrt{1-\left|\mathbf{R}^{\prime}{ }_{\perp}\right|^2} \mathbf{n}
\end{aligned}
$ \mathbf Rは長さ$ 1と考えられる
まず$ \mathbf{R}^{\prime}{ }_{\perp}から
$ + \cos\theta \mathbf nは$ \mathbf Rから垂直な成分を取り除いていると解釈できる
つまり、$ (\mathbf{R}+\cos \theta \mathbf{n})は長さ$ \sin \thetaを持つ
それに対して、$ \frac{\eta}{\eta^{\prime}}を掛けることで、$ \mathbf R'_{\perp}の長さは$ \sin\theta'となる。
$ \mathbf{R}^{\prime}{ }_{/ /}の長さは$ \cos\theta'となる。
従って、このベクトルは法線に対して角度$ \theta'をなしている。
$ \mathbf{R}^{\prime}{ }_{\perp}=\frac{\eta}{\eta^{\prime}}(\mathbf{R}+\cos \theta \mathbf{n})について
$ \cos\thetaの値がよくわからないので既知の値で表したい
$ \cos\theta = -\mathbf R \cdot \mathbf nなので
$ \mathbf{R}^{\prime}{ }_{\perp}=\frac{\eta}{\eta^{\prime}}(\mathbf{R}+ -(\mathbf R \cdot \mathbf n) \mathbf{n})
というわけでこれらのベクトルは$ \mathbf{R}^{\prime}{ }_{\perp} + \mathbf{R}^{\prime}{ }_{/ /}で求められる。
というわけでこのように反射させるマテリアルを実装した
屈折率$ \frac{\eta}{\eta^{\prime}} = 1.0/1.5
https://scrapbox.io/files/66b336c970cd01001cd907d7.png
なんで内部の景色が反転するのだろうか?
$ \sin \theta^{\prime}=\frac{\eta}{\eta^{\prime}} \cdot \sin \theta
https://scrapbox.io/files/66b33d2c9908de001ce61fea.jpeg
下のレイに注目
$ \frac{\eta}{\eta^{\prime}} < 1なので、点線よりも"内側"に新しいレイを打つ傾向にある
法線に近い側に打つと言った方が正確か?
なので空が見えやすくなる
逆に、$ \frac{\eta}{\eta^{\prime}} > 1だったら法線より遠い側に打つ
なので地面が見えやすくなる
反射光と屈折光に分かれるんじゃなかったっけappbird.icon 11.3. Total Internal Reflection
One troublesome practical issue with refraction is that there are ray angles for which no solution is possible using Snell's law.
90度を超える角度で屈折するケースがある(内部反射)
解が存在しない場合($ \eta/\eta' \cdot \sin\theta > 1.0)に限り、全反射を起こす(鏡面反射する) イメージが掴めない場合はこれがわかりやすいappbird.icon
というわけでこれを実装した
けど現れる内容は変わらない
---> 現状が$ \eta/\eta' > 1.0でないため、全反射を起こすことがない
パラメータを変えてみよう
出射側屈折率1.0、入射側屈折率1.3(水の中の泡)
https://scrapbox.io/files/66b337ff467f01001c2dbd70.png
確かに一部分で反射を起こしている
上下反転を起こしていないことに注意
反射を起こしている部分とカメラの位置の関係性を考えてみると、$ \eta/\eta' \sin \thetaは確かに大きめの角度で、$ 1.0を超えそう
ほとんどの部分は$ \sin\theta'は小さめの値になるのであまり強く屈折しなさそう
ガラスを再現したい
つまり、ある角度から入射した光は屈折しやすい一方で、別の角度から入射した光は反射しやすい。
一応厳密な式はある
code:tex
\begin{aligned}
& R_s=\left|\frac{\eta_1 \cos \theta_i-\eta_2 \sqrt{1-\left(\frac{\eta_1}{\eta_2} \sin \theta_i\right)^2}}{\eta_1 \cos \theta_i+\eta_2 \sqrt{1-\left(\frac{\eta_1}{\eta_2} \sin \theta_i\right)^2}}\right|^2, \\
& R_p=\left|\frac{\eta_1 \sqrt{1-\left(\frac{\eta_1}{\eta_2} \sin \theta_i\right)^2}-\eta_2 \cos \theta_i}{\eta_1 \sqrt{1-\left(\frac{\eta_1}{\eta_2} \sin \theta_i\right)^2}+\eta_2 \cos \theta_i}\right|^2 .
\end{aligned}
s ... 進行方向に垂直
p ... 進行方向に平行
$ R\left(\theta_i\right)=R_0+\left(1-R_0\right)\left(1-\cos \theta_i\right)^5
11.5 Modeling a Hollow Glass Sphere
中空のグラスを再現する
これできるん?
あーガラスの中にもう一個屈折体として空気を置くのか
https://scrapbox.io/files/66b34447b220dc001c2571d4.png
いけた ガラスに見えてすごappbird.icon
WJ.icon青い球がうっすらとガラスの中に見えるのはなんでだろう
AC.icon 12. Positionable Camera
デバッグするの面倒なので段階的に作るぞ
https://scrapbox.io/files/66b34a1ffada37001cda29ea.png
そしてカメラを自由に動かせるように
自分もラスタライザ作る時はこうしようかなappbird.icon https://scrapbox.io/files/66b3504721becf001d6ea3e4.png
あまり光線が反射しないためか、素早く計算が終わったappbird.icon
14.4s
vfovを20度にする
https://scrapbox.io/files/66b3509e8c653d001c3c0ea5.png
おぉ...確かに
AC.icon 13. Defocus Blur
最後だ!
Now our final feature: defocus blur. Note, photographers call this depth of field, so be sure to only use the term defocus blur among your raytracing friends. We call the distance between the camera center and the plane where everything is in perfect focus the focus distance.
しかしこれどうやって実装するんだろ、適当に光線ねじ曲げるんか?appbird.icon
lookfromにレンズをおいてviewportの狙いのピクセルに向けてレイを放つらしい
これ、現実のカメラと等価なのか...?appbird.icon
レンズによる光の曲げの作用が入ってないけど無視してええんかな? いや、レンズのどこか一点からviewportの特定のピクセルに向けてレイが放たれているという状況自体にレンズの曲げの作用が入っているのか
じゃあ何が違和感なんだろう
ピンホールカメラはViewportを前に持ってきても問題なかった
それは光の飛んでくる位置に対称性があったから
あ、わかったappbird.icon
viewportを乾板としてみなすわけじゃなくて、レンズにおいて焦点が合わせられる平面とみなすわけか
別にviewport上で色を捉えてもいいもんね(viewportはもはや乾板ではないし、そのようにしてもカメラのシステムにはなんら影響を及ぼさない)
そしたら、それらの集めた光は最終的にレンズによって乾板上の特定の一点にまとめられる
---> それらの色の算術平均を取ればOKで、別にレンズの動きを再現する必要はない
ただし、そのまとめによって上下が反転するので、適宜入れ替える
もしその焦点よりも近い・遠い位置にオブジェクトがあったら色は集中せず、ボケる
なるほどかしこい
ちゃんとボケてる!!すごい!!!
https://scrapbox.io/files/66b3794f81cc51001d4de6b1.png
AC.icon 14. Where Next?
目的:タイトルに映ってた風景撮ろうや
おおおおお!!!!!!!!!!!!!!!!!!!!!!!!!!
マジでC++コード書くだけでこれできるんだ........ 感動........appbird.icon https://scrapbox.io/files/66b37eb24a9530001d7e73a4.png
72s
完
というわけにもいかないので、ちゃんとレンダリングをする
width 640, samples_per_pixel = 300
1 hour 38min
https://scrapbox.io/files/66b3a23d8645db001d45c9db.png
https://scrapbox.io/files/66b39f252323f7001cc29f91.png
せっかくなので真ん中のガラス球を中空にしてみたappbird.icon
おわり!
光線の動きプログラムしたらちゃんと絵が描けるのすっごい面白い...!!!
なんでいままでこれをやってなかったんだろうと思いたくなる
が、学部で学んだ内容と色々結びついた部分もあるのでちょうど今でよかったかなとも思った